const WebSocket = require('ws')
const protobuf = require('protobufjs')
const zlib = require('zlib')
const protobufjson = require('./protobuf.json')

const { homedir, platform } = require('os')
const { join } = require('path')
const { existsSync, readFileSync, appendFileSync, writeFileSync } = require('fs')
let homeDir = homedir()
let platform_ = platform()
if (platform_ == 'win32') {
  homeDir = `${homeDir}/Desktop`
}
console.warn(`当前系统日志文件存储路径-->>>${platform_}`, homeDir)

let CommonMessageData = protobufjson.CommonMessageData
let pushproto = protobufjson.pushproto
let RoomDataSyncMessage = protobufjson.RoomDataSyncMessage
let WebcastChatMessage = protobufjson.WebcastChatMessage
let WebcastRoomMessage = protobufjson.WebcastRoomMessage
let WebcastRoomIntroMessage = protobufjson.WebcastRoomIntroMessage
let WebcastGiftMessage = protobufjson.WebcastGiftMessage
let WebcastMemberMessage = protobufjson.WebcastMemberMessage
let WebcastRoomUserSeqMessage = protobufjson.WebcastRoomUserSeqMessage
let WebcastLikeMessage = protobufjson.WebcastLikeMessage
let root = protobuf.Root.fromJSON(pushproto)

// let endpoint = 'wss://webcast100-ws-c.amemv.com/webcast/im/push/v2/?is_guest_mode=0&app_type=normal&room_tag=video&channel=update&is_android_pad=0&device_type=MI%206&language=zh&cpu_support64=true&live_cursor=0&rid=7207088902128126759&resolution=1080*1776&update_version_code=20709900&im-sdk-version=2&cdid=0a24c3ed-727b-4f00-91f0-c1b7acd24086&minor_status=0&need_persist_msg_count=12&parse_cnt=0&ac=wifi&package=com.ss.android.ugc.aweme&current_network_quality_info=%7B%7D&device_id=3450681289351447&compress=gzip&recv_cnt=0&version_code=200700&support_cache=1&im-x-tt-token=&resp_content_type=protobuf&live_id=1&address_book_access=2&user_id=0&device_platform=android&aid=1128&cursor=0&room_id=7207088902128126759&last_rtt=0&manifest_version_code=200701&_rticket=1678100311371&webcast_gps_access=1&iid=3186798498154926&is_landscape=false&host_abi=armeabi-v7a&openudid=59873a09832b2402&sid=&is_pad=false&appTheme=light&os_api=28&identity=audience&webcast_locale=zh_CN&dpi=480&oaid=9e6340801d21ffa3&carrier_region=CN&os=android&os_version=9&support_wrds=1&webcast_sdk_version=2650&app_name=aweme&version_name=20.7.0&device_brand=Xiaomi&need_personal_recommend=1&webcast_language=zh&ts=1678100311&which_ws=live_ws&version_code=200700&iid=3186798498154926&platform=0&fpid=9&sdk_version=2&transport_mode=0&device_id=3450681289351447&service_id_list=%5B%5D&private_protocol_enable=false&aid=1128&ttnet_ignore_offline=1&access_key=FD3326984D66D22AD6D6660BE3E817E5&ne=1'
// let map_wss = []
// for (let index = 0; index < 10000; index++) {
//     let ws1 = new WebsocketBuild('wss://m.superbest.wang/wss/')
//     map_wss.push(ws1)
// }
// // let ws1 = new WebsocketBuild(endpoint)
// setInterval(() => {
//     if (map_wss.length > 0) {
//         let num = 0
//         for (const iterator of map_wss) {
//             if (iterator.ws.readyState == 1) {
//                 num++
//             }
//         }
//         console.warn('连接数量---->>>', num)
//     }
// }, 2000)

let unGzipMessage = root.lookup('pushproto.Response')
let MessageLoad = root.lookupType('pushproto.PushFrame')
let messageProto = root.lookup('pushproto.PushFrame')
let Message = root.lookupType('pushproto.PushFrame')
class WebsocketBuild {
  // websocket对象
  ws = ''
  // websocket地址
  wsServer = ''
  // 断线重连次数
  limitConnect = 100000
  // 累计断线重连次数
  timeConnect = 0
  // websocket binaryType
  binaryType = 'nodebuffer'
  // 定时器
  Interva = null
  // 直播间在线人数
  onlineUserForAnchor = 0
  // 房间聊天信息
  RoomMessage = []
  //url query 参数
  query = {}
  // 是否代理
  agent = null
  //发送Ack响应标记
  SendAck = true
  // 日志文件路径
  filepath = ``
  fileExists = false
  constructor(wsServer, agent) {
    this.wsServer = wsServer
    this.agent = agent
    this.webSocketStart(this.wsServer)
    // 解析url
    let parm_url = new URL(this.wsServer)
    for (const iterator of parm_url.searchParams) {
      this.query[iterator[0]] = iterator[1]
    }
  }
  destroyWebSocket = () => {
    if (this.ws) {
      this.limitConnect = 0
      this.ws.close()
    }
  }
  HeartBeat = () => {
    // 心跳 * 回应
    this.Interva = setInterval(() => {
      if (this.ws) {
        //CONNECTING：值为0，表示正在连接。
        //OPEN：值为1，表示连接成功，可以通信了。
        //CLOSING：值为2，表示连接正在关闭。
        //CLOSED：值为3，表示连接已经关闭，或者打开连接失败。
        if (this.ws.readyState === 1) {
          // let Message = root.lookupType('pushproto.PushFrame')
          let message = Message.create({
            payloadType: 'hb'
          })
          let sendada = Message.encode(message).finish()
          this.ws.send(sendada)
          // console.warn('发送心跳响应-----')
        }
        this.SendAck = true
      }
    }, 5000)
  }
  webSocketStart = () => {
    let ClientOptions = {
      rejectUnauthorized: false,
      host: 'webcast100-ws-c.amemv.com',
      sessionTimeout: 500000,
      timeout: 500000
    }
    if (this.agent) {
      ClientOptions.agent = this.agent
      ClientOptions.headers = {
        Host: 'webcast100-ws-c.amemv.com',
        'User-Agent': 'okhttp/3.10.0.1'
      }
    }
    this.ws = new WebSocket(this.wsServer, ClientOptions)
    this.ws.binaryType = 'nodebuffer'
    this.ws.onmessage = this.onmessage
    this.ws.onclose = this.onclose
    this.ws.onopen = this.onopen
    this.ws.onerror = this.onerror
    this.ws.upgrade = this.upgrade
  }
  onmessage = (msg) => {
    // console.log('wss服务器消息')
    // let messageProto = root.lookup('pushproto.PushFrame')
    let message = null
    try {
      message = messageProto.decode(msg.data)
    } catch (error) {
      console.warn(error)
      return
    }
    try {
      if (message.headersList.compress_type == 'gzip' && this.SendAck) {
        // console.warn('解压前----->>>>', message.payload)
        const output = zlib.unzipSync(message.payload)
        // console.warn('解压后----->>>>', output)
        // let unGzipMessage = root.lookup('pushproto.Response')
        let gzipMessage = unGzipMessage.decode(output)
        if (gzipMessage.needAck) {
          // let MessageLoad = root.lookupType('pushproto.PushFrame')
          let payload = (function (e) {
            const t = []
            for (const o of e) {
              const e = o.charCodeAt(0)
              e < 128
                ? t.push(e)
                : e < 2048
                ? (t.push(192 + (e >> 6)), t.push(128 + (63 & e)))
                : e < 65536 &&
                  (t.push(224 + (e >> 12)), t.push(128 + ((e >> 6) & 63)), t.push(128 + (63 & e)))
            }
            return Buffer.from(t)
          })(gzipMessage.internalExt)
          // let message_res = MessageLoad.create({ payloadType: "ack", payload: payload, logid: message.logid });
          let headersList = []
          for (const key in message.headersList) {
            const element = message.headersList[key]
            headersList.push({
              [message.headersList[key]]: element
            })
          }
          let message_res = MessageLoad.create({
            payloadType: 'ack',
            payload: payload,
            logid: message.logid,
            headersList: JSON.stringify(headersList)
          })
          let sendada = MessageLoad.encode(message_res).finish()
          this.ws.send(sendada)
          // console.warn('发送ACK响应-----')
          this.SendAck = false
        }
        // this.AnalysisData(gzipMessage.messagesList)
      }
    } catch (error) {
      console.warn('解析数据错误~~~', error)
    }
  }
  // 数据解析
  AnalysisData = (data) => {
    try {
      let messagesList = data
      for (const iterator of messagesList) {
        // console.warn(iterator.method)
        let info = {}
        switch (iterator.method) {
          case 'WebcastRoomDataSyncMessage':
            let RoomDataSyncResult = protobuf.Root.fromJSON(RoomDataSyncMessage)
            let room_data_sync_message = RoomDataSyncResult.lookup('RoomDataSyncMessage')
            let RoomDataSyncData = room_data_sync_message.decode(iterator.payload)
            info = RoomDataSyncData.toJSON()
            // console.warn(info)
            if (info.user) {
              let msg = `${info.user.nickname} (${info.user.id})&sec_user_id=${info.user.secUid}`
              this.RoomMessage.push(msg)
            }
            break
          case 'WebcastChatMessage':
            let ChatMessageResult = protobuf.Root.fromJSON(WebcastChatMessage)
            let ChatMessage = ChatMessageResult.lookup('ChatMessages')
            let ChatMessageData = ChatMessage.decode(iterator.payload)
            // console.warn(ChatMessageData.toJSON())
            info = ChatMessageData.toJSON()
            let msg = `${info.user.nickname} (${info.user.id}):${info.content}&sec_user_id=${info.user.secUid}`
            this.RoomMessage.push(msg)
            break
          case 'WebcastRoomMessage':
            let WebcastRoomResult = protobuf.Root.fromJSON(WebcastRoomMessage)
            let webcast_roomMessage = WebcastRoomResult.lookup('WebcastRoomMessage')
            let MessageData = webcast_roomMessage.decode(iterator.payload)
            // console.warn(MessageData.toJSON())
            info = MessageData.toJSON()
            let msg2 = `${info.user.nickname} (${info.user.id}):进入直播间&sec_user_id=${info.user.secUid}`
            this.RoomMessage.push(msg2)
            break
          case 'WebcastRoomIntroMessage':
            let RoomIntroResult = protobuf.Root.fromJSON(WebcastRoomIntroMessage)
            let RoomIntroMessageData = RoomIntroResult.lookup('RoomIntro.RoomIntroMessage')
            let RoomIntroData = RoomIntroMessageData.decode(iterator.payload)
            // console.warn(RoomIntroData.toJSON())
            info = RoomIntroData.toJSON()
            let msg3 = ` ${info.user.nickname} (${info.user.id}):&sec_user_id=${info.user.secUid}`
            this.RoomMessage.push(msg3)
            break
          case 'WebcastGiftMessage':
            let GiftMessageResult = protobuf.Root.fromJSON(WebcastGiftMessage)
            let GiftMessageMessageData = GiftMessageResult.lookup('dy.GiftMessage')
            let GiftMessageData = GiftMessageMessageData.decode(iterator.payload)
            // console.warn(GiftMessageData.toJSON())
            info = GiftMessageData.toJSON()
            let msg4 = `${info.user.nickname} (${info.user.id}):送礼物&sec_user_id=${info.user.secUid}`
            this.RoomMessage.push(msg4)
            break
          case 'WebcastMemberMessage':
            let MemberMessageResult = protobuf.Root.fromJSON(WebcastMemberMessage)
            let MemberMessageData = MemberMessageResult.lookup('MemberMessage')
            let MemberData = MemberMessageData.decode(iterator.payload)
            // console.warn(MemberData.toJSON())
            info = MemberData.toJSON()
            let msg5 = `${info.user.nickname} (${info.user.id}):来了&sec_user_id=${info.user.secUid}`
            this.RoomMessage.push(msg5)
            break
          case 'WebcastLikeMessage':
            let LikeMessageResult = protobuf.Root.fromJSON(WebcastLikeMessage)
            let LikeMessageData = LikeMessageResult.lookup('LikeMessage')
            let LikeData = LikeMessageData.decode(iterator.payload)
            // console.warn(LikeData.toJSON())
            info = LikeData.toJSON()
            let msg6 = `${info.user.nickname} (${info.user.id}):点赞&sec_user_id=${info.user.secUid}`
            this.RoomMessage.push(msg6)
            break
          case 'WebcastRoomUserSeqMessage':
            let RoomUserSeqResult = protobuf.Root.fromJSON(WebcastRoomUserSeqMessage)
            let SeqMessageData = RoomUserSeqResult.lookup('RoomUserSeqMessage')
            let SeqData = SeqMessageData.decode(iterator.payload)
            // console.warn(SeqData.toJSON())
            info = SeqData.toJSON()
            // 当前直播间人数
            if (info.onlineUserForAnchor) {
              this.onlineUserForAnchor = info.onlineUserForAnchor
              onlineUserForAnchor.value = info.onlineUserForAnchor
              console.warn('当前直播间人数---->>>', this.onlineUserForAnchor)
            }
            break
          default:
            break
        }
      }
    } catch (error) {}
  }
  upgrade = () => {
    console.log('wss upgrade')
  }
  onclose = () => {
    // console.log('wss服务器已经断开')
    this.reconnect(this.wsServer)
  }
  onopen = () => {
    // console.log('已连接wss服务器')
    this.HeartBeat()
  }
  onerror = (error) => {
    // console.log('wss服务器error', error.message)
    this.TasksErrorLogs(this.wsServer, this.query, error.message)
  }
  reconnect = () => {
    // lockReconnect加锁，防止onclose、onerror两次重连
    if (this.limitConnect > 0) {
      this.limitConnect--
      this.timeConnect++
      // console.log('第' + this.timeConnect + '次重连')
      // 进行重连
      setTimeout(() => {
        this.webSocketStart(this.wsServer)
      }, 2000)
    } else {
      // console.log('wss连接已超时')
    }
  }
  /**
   * @param {*} url 连接地址
   * @param {*} query url参数
   * @param {*} msg   错误信息
   */
  async TasksErrorLogs(url, query, msg) {
    try {
      let agent = this.agent
      let room_id = query['room_id']
      if (!this.fileExists) {
        this.filepath = join(`${homeDir}/${room_id}_error.log`)
        this.fileExists = existsSync(this.filepath)
        if (!this.fileExists) {
          await writeFileSync(
            this.filepath,
            JSON.stringify({
              device_id: query['device_id'] || '',
              access_key: query['access_key'] || '',
              token: query['token'] || '',
              sessionId: query['sessionId'] || '',
              agent: agent
                ? agent.proxy['host'] + ':' + agent.proxy['port'] + ':' + agent.proxy['auth']
                : ''
            }) +
              '==' +
              msg,
            { encoding: 'utf8' }
          )
        }
        this.fileExists = true
      }
      if (this.filepath) {
        await appendFileSync(
          this.filepath,
          JSON.stringify({
            device_id: query['device_id'] || '',
            access_key: query['access_key'] || '',
            token: query['token'] || '',
            sessionId: query['sessionId'] || '',
            agent: agent
              ? agent.proxy['host'] + ':' + agent.proxy['port'] + ':' + agent.proxy['auth']
              : ''
          }) +
            '==' +
            msg,
          { encoding: 'utf8' }
        )
      }
    } catch (error) {
      console.warn('写入日志文件失败~~~', JSON.stringify(error))
    }
  }
}

module.exports = {
  WebsocketBuild
}
